package com.h2cloud.depends;

import org.springframework.beans.BeanUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

/**
 * @author: wulongkai
 * @create: 2022-10-11 10:39
 **/
public class BeanCopyUtil extends BeanUtils {
    /**
     * 集合数据的拷贝
     *
     * @param sources: 数据源类
     * @param target:  目标类::new(eg: UserVO::new)
     * @return
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }


    /**
     * 带回调函数的集合数据的拷贝（可自定义字段拷贝规则）
     *
     * @param sources:  数据源类
     * @param target:   目标类::new(eg: UserVO::new)
     * @param callBack: 回调函数
     * @return
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanUtilCopyCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
        }
        return list;
    }

    public static String[] getNullPropertyNames(Object source) {
//        final BeanWrapper src = new BeanWrapperImpl(source);
//        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
//
//        Set<String> emptyNames = new HashSet<String>();
//        for (java.beans.PropertyDescriptor pd : pds) {
//            if(!"customFieldList".equals(pd.getName())){
//                Object srcValue = src.getPropertyValue(pd.getName());
//                if (srcValue == null) emptyNames.add(pd.getName());
//            }
//        }
//
//        String[] result = new String[emptyNames.size()];
//        return emptyNames.toArray(result);

        List<String> nullValuePropertyNames = new ArrayList<>();
        for (Field f : source.getClass().getDeclaredFields()) {
            try {
                f.setAccessible(true);
                if (f.get(source) == null) {
                    nullValuePropertyNames.add(f.getName());
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return nullValuePropertyNames.toArray(new String[0]);
    }

    /**
     * 拷贝数据对象忽略null字段
     *
     * @param src
     * @param target
     */
    public static void copyPropertiesExcludeNull(Object src, Object target) {
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }

    /**
     * 拷贝数据对象忽略null字段
     *
     * @param src
     * @param target
     */
    public static void copyPropertiesExcludeNull(Object src, Object target, String... ignoreProperties) {
        String[] arr = getNullPropertyNames(src);
        List<String> list = new ArrayList<>();
        list.addAll(Arrays.asList(arr));
        if (Objects.nonNull(ignoreProperties)) {
            for (int i = 0; i < ignoreProperties.length; i++) {
                list.add(ignoreProperties[i]);
            }
        }
        BeanUtils.copyProperties(src, target, list.toArray(new String[list.size()]));
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, EmployeeNoEffectiveFunction<Long, T, Exception> bFun, Long param1) throws Exception {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = bFun.apply(param1);
            copyPropertiesExcludeNull(source, t);
            list.add(t);
        }
        return list;
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, EmployeeFunction<Long, Long, T, Exception> bFun, Long param1, Long param2) throws Exception {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = bFun.apply(param1, param2);
            copyPropertiesExcludeNull(source, t);
            list.add(t);
        }
        return list;
    }

    /**
     * 利用序列化深度克隆
     *
     * @param src
     * @return
     * @throws Exception
     */
    public static Object clone(Object src) throws Exception {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        return in.readObject();
    }
}

